/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package music.Chords;

import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import music.Note;
import music.types.BaseNotes;
import music.types.Interval;
import music.types.IntervalHolder;
import music.types.MidiNote;
import music.types.NoteTypes;

/**
 *
 * @author TJ
 */
public class Chord extends Note{
    private ChordInfo template;
    private int inv=1;
    public Chord(NoteTypes n){super(n);init();}
    public Chord(NoteTypes n,String n2){super(n);init();parseChord(n2);}
    public Chord(NoteTypes n, MidiNote r){
        super(n);
        init();
        template.setRoot(r);
        constructChord();
    }
    public void invert(){
        
    }
    public void shiftOctave(int shift){
     for(MidiNote n : this.getNotes()) {
         n.shiftNote(12*shift);
     }   
    }
    private void init() {
        template=new ChordInfo();
    }
    public void constructChord() {
        MidiNote root=template.getRoot();
       getNotes().add(root);
       IntervalHolder t;
       if(template.getThird()==IntervalQuality.SUS_2) {
           t=new IntervalHolder(Interval.getInterval(IntervalQuality.MAJOR, 2),2);
        getNotes().add(t.getNoteAbove(root));
       }else if(template.getThird()==IntervalQuality.SUS_4){
           t=new IntervalHolder(Interval.getInterval(IntervalQuality.MAJOR, 4),4);
        getNotes().add(t.getNoteAbove(root));
       }else if(template.getThird()!=IntervalQuality.NONE) {
         t=new IntervalHolder(Interval.getInterval(template.getThird(), 3),3);
        getNotes().add(t.getNoteAbove(root));
       }
       if(template.getFifth()!=IntervalQuality.NONE) {
        t=new IntervalHolder(Interval.getInterval(template.getFifth(), 5),5);
        getNotes().add(t.getNoteAbove(root));
       }
       for(IntervalHolder i : template.getAddedIntervals()) {
           getNotes().add(i.getNoteAbove(root));
       }
    }
    public void setChordInfo(ChordInfo c){template=c;constructChord();}
    public static Chord parse(String s){return parse(NoteTypes.WHOLE,s);}
    public static Chord parse(NoteTypes n,String s){
        Chord c=new Chord(n);
        c.parseChord(s);
        return c;}
    private void parseChord(String s) {
        String reg="^[A-G](#|b)?(m|M|+|dim)?((m|M)?(2|4|6|7|9|11|13))?((b|#)5)?(\\((b|#)?(7|9|11|13)\\))(no3|sus|sus2|sus4)?$";
        ChordInfo c=new ChordInfo();
        s=getRoot(s,c);
        s=getChordIntervalQuality(s,c);
        s=getFifth(s,c);
        s=getAddedNote(s,c);
        s=getSuspended(s,c);
        this.setChordInfo(c);
    }
    private String getRoot(String s, ChordInfo c) {
        MidiNote b=BaseNotes.valueOf((""+s.charAt(0)).toUpperCase()).getNote();
        if(s.length()==1){
            c.setRoot(new MidiNote(b.getMidi(),b.getName()));
            return s.substring(1);
        }
        char flat=s.charAt(1);
        if(flat=='b'){
            c.setRoot(new MidiNote(b.getMidi()-1,b.getName()+flat));
            return s.substring(2);
        }else if(flat=='#'){
            c.setRoot(new MidiNote(b.getMidi()+1,b.getName()+flat));
            return s.substring(2);
        }else{
            c.setRoot(new MidiNote(b.getMidi(),b.getName()));
        }
         return s.substring(1);
        
    }
    private String getAddedNote(String s, ChordInfo c){
        Matcher m=Pattern.compile("\\((b|#)?(2|4|6|7|9|10|11|13)\\)").matcher(s);
        //m.matches();
        if(m.lookingAt() && m.start()==0){
            String g=m.group().substring(1, s.length()-1);
            int addsemi=0;
            if(s.charAt(1)=='b'){
                g=g.substring(1);
                addsemi=-1;
            }else if(s.charAt(1)=='#'){
                g=g.substring(1);
                addsemi=1;
            }
            int scale=Integer.parseInt(g);
            c.addInterval(new IntervalHolder(
                    Interval.findBySemitones(Interval.getInterval(IntervalQuality.MAJOR, scale).getSemitones()+addsemi)
                    ,scale));
    
            s=s.substring(m.end());
        }
        return s;
    }
    private String getFifth(String s,ChordInfo c){
        Matcher m=Pattern.compile("(#5|b5)").matcher(s);
        //m.matches();
        if(m.lookingAt() && m.start()==0){
            if(m.group().charAt(0)=='#'){
                c.setFifth(IntervalQuality.AUGMENTED);
            }else {
                c.setFifth(IntervalQuality.DIMINISHED);
            }
            s=s.substring(m.end());
        }
        return s;
    }
    private String getSuspended(String s,ChordInfo c) {
         Matcher m=Pattern.compile("(sus2|sus4|sus|no3)").matcher(s);
        //m.matches();
        if(m.lookingAt() && m.start()==0){
            
            c.setThird(IntervalQuality.getQualityFromString(m.group()));
            s=s.substring(m.end());
        }
        return s;
    }
    private String getChordIntervalQuality(String s, ChordInfo c) {
        ChordQuality q=ChordQuality.MAJOR;
        IntervalQuality i=IntervalQuality.MINOR;
        Matcher m=Pattern.compile("(ø|dom|M|maj|m|min|aug|dim)").matcher(s);
        //m.matches();
        if(m.lookingAt() && m.start()==0){
            q=ChordQuality.getQualityFromString(m.group());
            i=IntervalQuality.getDefaultQualityFromChordQuality(q);
            c.setChordQuality(q);
            s=s.substring(m.end());
        } else{
            s=getChordInterval(s,c,false,i);
            return s;}
        m=Pattern.compile("(M|maj|m|min)").matcher(s);
        boolean reqd=false;
        if(m.lookingAt() && m.start()==0){
            reqd=true;
            i=IntervalQuality.getQualityFromString(m.group());
            s=s.substring(m.end());
        }
        s=getChordInterval(s,c,reqd,i);
            
        return s;
        
    }
    private IntervalHolder getChordInterval(int inter, ChordQuality q, IntervalQuality iq, int chordInter) {
        if(inter==7) {
            return new IntervalHolder(Interval.getInterval(iq, 7),7);
        }else if(chordInter!=13 && (inter==9 || inter==11)){
            if(q==ChordQuality.DIMINISHED || q==ChordQuality.DIMINISHED) {
                if(inter==9) {
                return new IntervalHolder(Interval.MINOR_NINTH,9);
                }else {return new IntervalHolder(Interval.MAJOR_TENTH,11);}
            }
            else {
                if(inter==9) {return new IntervalHolder(Interval.MAJOR_NINTH,9);}
                else {return new IntervalHolder(Interval.PERFECT_ELEVENTH,11);}
            }
        } else if(chordInter==13){
            if(inter==11){return new IntervalHolder(Interval.PERFECT_ELEVENTH,11);}
            return new IntervalHolder(Interval.getInterval(IntervalQuality.MAJOR, inter),inter);
        }
        return null;
    }
    private String getChordInterval(String s,ChordInfo c, boolean reqd,IntervalQuality q){
       Matcher m=Pattern.compile("(2|4|6|7|9|11|13)").matcher(s);
           
            if(m.lookingAt() && m.start()==0) {
                int i=Integer.parseInt(m.group());
                if(i<7){q=IntervalQuality.MAJOR;}
                s=s.substring(m.end());
                if(i>=7) {
                    for(int j=7;j<=i; j+=2) {
                        c.addInterval(getChordInterval(j,c.getChordQuality(),q,i));
                    }
                }else {
                    c.addInterval(new IntervalHolder(Interval.getInterval(q,i),i));
                }
            } else if(reqd){
                //throw new Exception();}
            }
            return s;
    }
    
    public static Chord powerChord(NoteTypes n,String root) {
        Chord c=new Chord(n);
        ChordInfo c2=new ChordInfo();
        c2.setThird(IntervalQuality.NONE);
        c2.setRoot(root);
        c2.getAddedIntervals().clear();
        c2.setFifth(IntervalQuality.MAJOR);
        c.setChordInfo(c2);
        return c;
    }
}
